package com.xiaoxin.codesandbox;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import com.xiaoxin.codesandbox.modle.ExecuteCodeRequest;
import com.xiaoxin.codesandbox.modle.ExecuteCodeResponse;
import com.xiaoxin.codesandbox.modle.ExecuteMessage;
import com.xiaoxin.codesandbox.modle.JudgeInfo;
import com.xiaoxin.codesandbox.utils.ProcessUtil;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

@Component
public class JavaCodeSandBoxTemplate implements CodeSandBox{
    public static void main(String[] args) {
        ExecuteCodeRequest executeCodeRequest = new ExecuteCodeRequest();
        executeCodeRequest.setLanguage("java");
        executeCodeRequest.setCode(ResourceUtil.readStr("testCode/Main.java", StandardCharsets.UTF_8));
        executeCodeRequest.setInputList(Arrays.asList("1 2", " 3 4"));
        JavaNativeCodeSandBoxImplOld codeSandBox = new JavaNativeCodeSandBoxImplOld();
        ExecuteCodeResponse executeCodeResponse = codeSandBox.doExecute(executeCodeRequest);
        System.out.println(executeCodeResponse);
    }

    @Override
    public ExecuteCodeResponse doExecute(ExecuteCodeRequest executeCodeRequest) {
        String code = executeCodeRequest.getCode();
        List<String> inputList = executeCodeRequest.getInputList();
        /**
         * 将用户代码写入到单独文件夹
         */
        File userCodetoFile = userCodetoFile(code);
        /**
         * 将用户代码编译为class文件
         */
        ExecuteMessage executeCompileMessage = compileUserCode(userCodetoFile);
        System.out.println(executeCompileMessage);
        /**
         * 运行用户的class代码
         */
        List<ExecuteMessage> executeMessageList = runUserCode(inputList, userCodetoFile);
        /**
         * 收集信息
         */
        ExecuteCodeResponse executeCodeResponse = searchMessage(executeMessageList);
        System.out.println(executeCodeResponse);
        /**
         * 清除文件
         */
        clearCodeFile(userCodetoFile);
        return executeCodeResponse;
    }

    /**
     * 将用户代码写入到单独文件夹中
     * @param code
     * @return
     */
    public File userCodetoFile(String code){
        //用户目录
        String userPath = System.getProperty("user.dir");
        //存放所有用户的代码
        String globalCodePath = userPath + File.separator + "tempCode";
        if (!FileUtil.exist(globalCodePath)) {
            FileUtil.mkdir(globalCodePath);
        }
        //存放每一个用户代码的父目录
        String parentCodePath = globalCodePath + File.separator + UUID.randomUUID();
        //每一个用户的代码文件
        String userCodePath = parentCodePath + File.separator + "Main.java";
        //将用户代码写入文件
        File userCodeFile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);
        return userCodeFile;
    }

    /**
     * 将用户代码编译为class文件
     * @param userCodeToFile
     * @return
     */
    public ExecuteMessage compileUserCode(File userCodeToFile){
        String compiledCmd = String.format("javac -encoding utf-8 %s", userCodeToFile.getAbsolutePath());
        Process compileProcess;
        try {
            compileProcess = Runtime.getRuntime().exec(compiledCmd);
        } catch (IOException e) {
            throw new RuntimeException();
        }
        ProcessUtil processUtil = new ProcessUtil();
        ExecuteMessage executeCompileMessage = processUtil.RunProcessAndGetResult(compileProcess, "编译");
        return executeCompileMessage;
    }

    /**
     * 运行用户的class代码
     * @param inputList
     * @param userCodeToFile
     * @return
     */
    public List<ExecuteMessage> runUserCode(List<String> inputList,File userCodeToFile){
        String parentCodePath = userCodeToFile.getParentFile().getAbsolutePath();
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        for (int i = 0; i < inputList.size(); i++) {
            String runCmd = String.format("java -Xmx256m -Dfile.encoding=UTF-8 -cp %s Main %s", parentCodePath, inputList.get(i));
            Process runProcess;
            try {
                runProcess = Runtime.getRuntime().exec(runCmd);
                //守护线程
                new Thread(() -> {
                    try {
                        Thread.sleep(5000);
                        System.out.println("超时中断");
                        runProcess.destroy();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }).start();
            } catch (IOException e) {
                throw new RuntimeException();
            }
            ProcessUtil processUtil = new ProcessUtil();
            ExecuteMessage executeRunMessage = processUtil.RunProcessAndGetResult(runProcess, "运行");
            System.out.println(executeRunMessage);
            executeMessageList.add(executeRunMessage);
        }
        return executeMessageList;
    }

    /**
     * 收集信息
     * @param executeMessageList
     * @return
     */
    public ExecuteCodeResponse searchMessage(List<ExecuteMessage> executeMessageList){
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        List<String> outputList = new ArrayList<>();
        long costTimeMax = 0;
        for (int i = 0; i < executeMessageList.size(); i++) {
            ExecuteMessage executeMessage = executeMessageList.get(i);
            if (executeMessage.getErrorMessage() != null) {
                executeCodeResponse.setStatus(3);
                return executeCodeResponse;
            }
            costTimeMax = Math.max(costTimeMax, executeMessage.getCostTime());
            outputList.add(executeMessage.getMessage());
        }
        executeCodeResponse.setOutputList(outputList);
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setCostedTime(costTimeMax);
        executeCodeResponse.setStatus(2);
        executeCodeResponse.setJudgeInfo(judgeInfo);
        return executeCodeResponse;
    }

    /**
     * 清除文件
     * @param userCodeTOFile
     */
    public void clearCodeFile(File userCodeTOFile){
        String parentCodePath = userCodeTOFile.getParentFile().getAbsolutePath();
        boolean del = FileUtil.del(parentCodePath);
        if (del) {
            System.out.println("删除成功");
        } else {
            System.out.println("删除失败");
        }
    }

    /**
     * 错误处理
     * @param e
     * @return
     */
    private ExecuteCodeResponse getErrorExecuteMessage(Exception e) {
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        executeCodeResponse.setOutputList(new ArrayList<String>());
        executeCodeResponse.setStatus(3);
        executeCodeResponse.setJudgeInfo(new JudgeInfo());
        executeCodeResponse.setMessage(e.getMessage());
        return executeCodeResponse;
    }
}
